{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "GE91qWZkm8ZQ"
      },
      "source": [
        "##### Copyright 2019 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "colab": {},
        "colab_type": "code",
        "id": "YS3NA-i6nAFC"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "7SN5USFEIIK3"
      },
      "source": [
        "# Word embeddings"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Aojnnc7sXrab"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/text/word_embeddings\">\n",
        "    <img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />\n",
        "    Xem trên TensorFlow.org</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/vi/tutorials/text/word_embeddings.ipynb\">\n",
        "    <img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />\n",
        "    Chạy trong Google Colab</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/vi/tutorials/text/word_embeddings.ipynb\">\n",
        "    <img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />\n",
        "    Xem mã nguồn trên GitHub</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/vi/tutorials/text/word_embeddings.ipynb\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\" />Tải notebook</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Note: Cộng đồng TensorFlow Việt Nam đã dịch các tài liệu này từ nguyên bản tiếng Anh.\n",
        "Vì bản dịch này dựa trên sự cố gắng từ các tình nguyện viên, nên không thể đám bảo luôn bám sát\n",
        "[Tài liệu chính thức bằng tiếng Anh](https://www.tensorflow.org/?hl=en).\n",
        "Nếu bạn có đề xuất để cải thiện bản dịch này, vui lòng tạo PR đến repository trên GitHub của [tensorflow/docs](https://github.com/tensorflow/docs)\n",
        "\n",
        "Để đăng ký dịch hoặc duyệt lại nội dung bản dịch, các bạn hãy liên hệ \n",
        "[docs@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Q6mJg1g3apaz"
      },
      "source": [
        "Bài hướng dẫn này sẽ xoay quanh chủ đề về word embedding. Nội dung của bài hướng dẫn sẽ bao gồm code để huấn luyện word embedding trên tập dữ liệu nhỏ và trực quan hóa các embedding thu được với [Embedding Projector](http://projector.tensorflow.org) (xem hình minh họa bên dưới).\n",
        "\n",
        "<img src=\"https://github.com/tensorflow/docs/blob/master/site/en/tutorials/text/images/embedding.jpg?raw=1\" alt=\"Screenshot of the embedding projector\" width=\"400\"/>\n",
        " \n",
        "## Biểu diễn văn bản dưới dạng số\n",
        "\n",
        "Đầu vào của các mô hình máy học là tập hợp các vector (mảng các số). Để đưa một đoạn văn bản vào mô hình, điều đầu tiên chúng ta cần làm là chuyển đổi các ký tự sang số (thực hiện vector hóa văn bản). Trong phần này, chúng ta sẽ đi qua ba cách để thực hiện mục tiêu trên.\n",
        "\n",
        "### One-hot encoding\n",
        "\n",
        "Cách làm đầu tiên chúng ta có thể nghĩ tới là encode \"one-hot\" từng từ trong danh sách từ vựng. Xét câu sau: \"The cat sat on the mat\". Danh sách từ vựng (hay tập hợp các từ phân biệt) trong câu trên là (cat, mat, on, sat, the). Để biểu diễn mỗi từ, ta tạo một vector 0 có độ dài bằng với số lượng từ trong danh sách từ vựng và thay 1 vào vị trí tương ứng của từ cần biểu diễn. Cách làm này được minh họa bằng sơ đồ bên dưới.\n",
        "\n",
        "<img src=\"https://github.com/tensorflow/docs/blob/master/site/en/tutorials/text/images/one-hot.png?raw=1\" alt=\"Diagram of one-hot encodings\" width=\"400\" />\n",
        "\n",
        "Để  thu được vector encode của một câu, ta chỉ việc nối các vector one-hot của từng từ trong câu.\n",
        "\n",
        "Nhận xét: Cách làm này không hiệu quả vì các vector thu được từ phương pháp one-hot encoding đều là các vector thưa (nghĩa là hầu hết các phần tử đều là 0). Nếu danh sách từ vựng có 10,000 từ thì để one-hot encoding một từ bất kì, ta cần tạo ra một vector mà ở đó 99.9% các phần tử đều là 0.\n",
        "\n",
        "### Encode mỗi từ bằng một số phân biệt\n",
        "\n",
        "Cách làm thứ hai mà ta có thể sử dụng là encode mỗi từ bằng một số phân biệt. Xét ví dụ trên, ta có thể sử dụng số 1 để encode cho từ \"cat\", số 2 cho từ \"mat\" và tương tự đối với các từ còn lại. Lúc này, cả câu \"The cat sat on the mat\" có thể được encode bằng một vector dày đặc, chẳng hạn [5, 1, 4, 3, 5, 2]. Cách làm này mang lại hiệu quả hơn vì kết quả thu được của ta là vector dày đặc thay vì vector thưa như cách làm trước.\n",
        "\n",
        "Tuy nhiên, cách làm này có hai nhược điểm:\n",
        "\n",
        "* Các số được sử dụng để encode là hoàn toàn ngẫu nhiên (do đó, không thể hiện được mối quan hệ giữa các từ với nhau).\n",
        "\n",
        "* Cách encode này có thể gây nhầm lẫn cho mô hình. Ví dụ trong mô hình phân loại tuyến tính, với mỗi đặc trưng mô hình cần học một trọng số tương ứng. Tuy nhiên vì hai từ tương đồng nhau chưa chắc cách encode của chúng sẽ tương tự nhau, trọng số mà mô hình học được từ sự tương đồng trong cách biểu diễn bằng số của các từ sẽ không có ý nghĩa.\n",
        "\n",
        "### Word embedding\n",
        "\n",
        "Word embedding là một phương pháp biểu diễn từ hiệu quả bằng các vector dày đặc cho phép sự tương đồng giữa các từ được phản ánh qua cách encode của nó. Đặc biệt hơn nữa, chúng ta sẽ không phải trực tiếp xác định cách encode của mỗi từ. Mỗi embedding là một vector số thực dày đặc với độ dài tùy chọn. Nếu xem các giá trị trong embedding như tham số thì ta hoàn toàn có thể huấn luyện mô hình để học các tham số này (tương tự như quá trình học trọng số trong các lớp Dense). Thông thường số chiều được chọn cho word embedding đối với tập dữ liệu nhỏ là 8 và có thể lên đến 1024 đối với tập dữ liệu lớn. Embedding có số chiều càng lớn thì càng có khả năng biểu diễn được những mối quan hệ phức tạp hơn giữa các từ, tuy nhiên cũng đòi hỏi một lượng lớn dữ liệu để học.\n",
        "\n",
        "<img src=\"https://github.com/tensorflow/docs/blob/master/site/en/tutorials/text/images/embedding2.png?raw=1\" alt=\"Diagram of an embedding\" width=\"400\"/>\n",
        "\n",
        "Hình trên là một ví dụ của word embedding. Mỗi từ được biểu diễn bởi một vector số thực 4 chiều. Ta có thể hình dung word embedding giống như một bảng tra. Sau khi mô hình đã học xong các trọng số (là các giá trị trong embedding), ta có thể encode mỗi từ bằng cách tra vào bảng để thu được vector dày đặc là biểu diễn của từ tương ứng."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "SZUQErGewZxE"
      },
      "source": [
        "## Chuẩn bị"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "SIXEk5ON5P7h"
      },
      "outputs": [],
      "source": [
        "!pip install tf-nightly\n",
        "import tensorflow as tf"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "RutaI-Tpev3T"
      },
      "outputs": [],
      "source": [
        "from tensorflow import keras\n",
        "from tensorflow.keras import layers\n",
        "\n",
        "import tensorflow_datasets as tfds\n",
        "tfds.disable_progress_bar()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "eqBazMiVQkj1"
      },
      "source": [
        "## Sử dụng lớp Embedding\n",
        "Thư viện Keras giúp việc sử dụng word embedding trở nên vô cùng đơn giản. Sau đây, chúng ta sẽ cùng tìm hiểu về [lớp Embedding](https://www.tensorflow.org/api_docs/python/tf/keras/layers/Embedding) được hỗ trợ trong thư viện này.\n",
        "\n",
        "Lớp Embedding có thể được hiểu như một bảng tra cho phép ánh xạ từ một chỉ mục (đại diện cho một từ nhất định) sang một vector dày đặc (embedding của từ đó). Số chiều (hay độ dài) của embedding là tham số mà bạn có thể tùy biến để phù hợp với bài toán của mình, tương tự như việc tùy biến số lượng neuron khác nhau trong một lớp Dense."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "-OjxLVrMvWUE"
      },
      "outputs": [],
      "source": [
        "embedding_layer = layers.Embedding(1000, 5)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "2dKKV1L2Rk7e"
      },
      "source": [
        "Các trọng số gắn với lớp Embedding sẽ được khởi tạo hoàn toàn ngẫu nhiên. Trong quá trình huấn luyện, các trọng số này sẽ dần được điều chỉnh thông qua cơ chế lan truyền ngược. Word embedding sau khi huấn luyện có thể phần nào encode được sự tương đồng giữa các từ.\n",
        "\n",
        "Với mỗi số nguyên được truyền vào (tương ứng với chỉ mục của từ cần encode), ta thu được một vector là embedding của từ ấy."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "0YUjPgP7w0PO"
      },
      "outputs": [],
      "source": [
        "result = embedding_layer(tf.constant([1,2,3]))\n",
        "result.numpy()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "O4PC4QzsxTGx"
      },
      "source": [
        "Đối với văn bản hay chuỗi văn bản liên tiếp, lớp Embedding nhận đầu vào là một tensor số nguyên 2 chiều (mỗi số nguyên tương ứng với chỉ mục của từ) có kích thước `(số lượng mẫu, độ dài chuỗi)`. Độ dài chuỗi truyền vào là tùy ý. Ví dụ, ta có thể đưa vào lớp embedding một batch với kích thước `(32, 10)` (32 chuỗi, mỗi chuỗi có độ dài là 10) hoặc `(64, 15)` (64 chuỗi, mỗi chuỗi có độ dài là 15).\n",
        "\n",
        "Tensor trả về sẽ nhiều hơn tensor đầu vào một chiều. Ví dụ, nếu ta truyền vào một batch có kích thước `(2, 3)` thì kết quả trả về sẽ là một tensor có kích thước `(2, 3, N)`. Chiều cuối cùng chính là embedding của các từ tương ứng."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "vwSYepRjyRGy"
      },
      "outputs": [],
      "source": [
        "result = embedding_layer(tf.constant([[0,1,2],[3,4,5]]))\n",
        "result.shape"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "WGQp2N92yOyB"
      },
      "source": [
        "Cụ thể hơn với đầu vào là một batch các chuỗi, lớp Embedding sẽ trả về kết quả là một tensor số thực 3 chiều với kích thước `(số lượng mẫu, độ dài chuỗi, số chiều embedding)`. Như đã lưu ý ở trên, `độ dài chuỗi` có thể tùy biến. Do đó, để chuyển đổi tất cả kết quả thu được sang một biểu diễn có số chiều cố định trước khi đưa vào lớp Dense, ta có thể áp dụng một số phương pháp sau: sử dụng RNN, Attention hoặc một lớp Pooling. Trong phạm vi của bài hướng dẫn, chúng tôi sẽ dùng phương pháp đơn giản nhất là sử dụng lớp Pooling. Ngoài ra, bạn có thể tìm hiểu thêm ở [Hướng dẫn phân loại văn bản với RNN](./text_classification_rnn.ipynb)."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "aGicgV5qT0wh"
      },
      "source": [
        "## Huấn luyện embedding"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "_Bh8B1TUT6mV"
      },
      "source": [
        "Tiếp theo chúng ta sẽ xây dựng một mô hình phân loại sắc thái từ các bình luận phim trên trang IMDB. Trong quá trình huấn luyện, mô hình sẽ học được các embedding cho các từ khác nhau. Ta sẽ tận dụng tập dữ liệu đã được tiền xử lý.\n",
        "\n",
        "Bạn có thể xem thêm về cách lấy các tập dữ liệu văn bản tại [Hướng dẫn lấy dữ liệu văn bản](../load_data/text.ipynb)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "yg6tyxPtp1TE"
      },
      "outputs": [],
      "source": [
        "(train_data, test_data), info = tfds.load(\n",
        "    'imdb_reviews/subwords8k', \n",
        "    split = (tfds.Split.TRAIN, tfds.Split.TEST), \n",
        "    with_info=True, as_supervised=True)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "jjnBsFXaLVPL"
      },
      "source": [
        "Trước tiên ta cần truy cập vào encoder của tập dữ liệu (`tfds.features.text.SubwordTextEncoder`) để xem danh sách các từ vựng hiện có.\n",
        "\n",
        "Kí hiệu \"\\_\" được dùng để biểu diễn khoảng trắng. Lưu ý rằng danh sách từ vựng sẽ bao gồm cả các từ toàn vẹn (kết thúc bằng \"\\_\") lẫn các từ không toàn vẹn (có thể ghép với nhau tạo thành từ có nghĩa): "
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "MYrsTgxhLBfl"
      },
      "outputs": [],
      "source": [
        "encoder = info.features['text'].encoder\n",
        "encoder.subwords[:20]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "GwCTfSG63Qth"
      },
      "source": [
        "Mỗi bình luận phim có thể có độ dài khác nhau. Do đó, chúng ta sẽ sử dụng `padded_batch` để chuẩn hóa độ dài của các bình luận."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "dRSnJkx4cs9P"
      },
      "outputs": [],
      "source": [
        "train_data"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "LwSCxER_2Lef"
      },
      "outputs": [],
      "source": [
        "train_batches = train_data.shuffle(1000).padded_batch(10, padded_shapes=([None],[]))\n",
        "test_batches = test_data.shuffle(1000).padded_batch(10, padded_shapes=([None],[]))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "upiYr1-Dc7CF"
      },
      "source": [
        "Lưu ý: Phiên bản **TensorFlow 2.2** không yêu cầu khai báo tham số padded_shapes. Ở chế độ mặc định, dữ liệu đầu vào sẽ được đệm thêm các phần tử sao cho dữ liệu ở mọi chiều đều có độ dài bằng nhau và bằng với độ dài lớn nhất theo chiều được xét."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "ZucJ_jzoc6Sv"
      },
      "outputs": [],
      "source": [
        "train_batches = train_data.shuffle(1000).padded_batch(10)\n",
        "test_batches = test_data.shuffle(1000).padded_batch(10)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "dF8ORMt2U9lj"
      },
      "source": [
        "Như mong đợi, các bình luận dưới dạng văn bản đã được encode thành các số nguyên (mỗi số nguyên đại diện cho một từ toàn vẹn hoặc không toàn vẹn trong danh sách từ vựng).\n",
        "\n",
        "Các số 0 được thêm vào ở cuối là kết quả của việc chuẩn hóa độ dài bằng với vector có độ dài lớn nhất."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "Se-phCknsoan"
      },
      "outputs": [],
      "source": [
        "train_batch, train_labels = next(iter(train_batches))\n",
        "train_batch.numpy()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "zI9_wLIiWO8Z"
      },
      "source": [
        "### Xây dựng mô hình đơn giản\n",
        "\n",
        "Để định nghĩa kiến trúc mô hình, ta sử dụng [API Sequential của Keras](../../guide/keras). Trong hướng dẫn này này, ta sẽ xây dựng một mô hình đơn giản theo kiểu \"Continous bag of words\":\n",
        "\n",
        "* Lớp Embedding nhận đầu vào là các chuỗi với mỗi từ đã được ký hiệu bằng một số nguyên và trả về vector embedding của từ tương ứng. Các vector này sẽ được học trong quá trình huấn luyện mô hình. Nói cách khác, đầu ra của lớp này sẽ có kích thước `(kích thước batch, độ dài chuỗi, số chiều embedding)`.\n",
        "\n",
        "* Với mỗi mẫu trong batch, lớp GlobalAveragePooling1D sẽ trả về một vector có kích thước cố thước cố định. Vector này là kết quả của việc tính trung bình theo từng chiều. Đây được xem là cách làm đơn giản nhất để  xử lý dữ liệu đầu vào có kích thước không cố định.\n",
        "\n",
        "* Các vector (đã chuẩn hóa kích thước) tiếp tục được đưa vào một lớp Dense gồm 16 unit ẩn.\n",
        "\n",
        "* Lớp cuối cùng được kết nối đầy đủ (fully-connected) tới một node đầu ra duy nhất với sigmoid làm hàm kích hoạt để thu được kết quả là một số thực nằm trong khoảng từ 0 đến 1. Số thực này đại diện cho xác suất (hay độ tin cậy) một bình luận mang ý nghĩa tích cực.\n",
        "\n",
        "Chú ý: Mô hình trên không sử dụng kỹ thuật mask. Do đó các phần tử 0 thêm vào trong quá trình chuẩn hóa độ dài cũng được xem như một phần của dữ liệu đầu vào. Kết quả đầu ra có thể bị ảnh hưởng bởi độ dài của phần đệm này. Để khắc phục, xem thêm [Hướng dẫn mask và đệm dữ liệu](../../guide/keras/masking_and_padding)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "pHLcFtn5Wsqj"
      },
      "outputs": [],
      "source": [
        "embedding_dim=16\n",
        "\n",
        "model = keras.Sequential([\n",
        "  layers.Embedding(encoder.vocab_size, embedding_dim),\n",
        "  layers.GlobalAveragePooling1D(),\n",
        "  layers.Dense(16, activation='relu'),\n",
        "  layers.Dense(1)\n",
        "])\n",
        "\n",
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "JjLNgKO7W2fe"
      },
      "source": [
        "### Biên dịch và huấn luyện mô hình"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "lCUgdP69Wzix"
      },
      "outputs": [],
      "source": [
        "model.compile(optimizer='adam',\n",
        "              loss=tf.keras.losses.BinaryCrossentropy(from_logits=True),\n",
        "              metrics=['accuracy'])\n",
        "\n",
        "history = model.fit(\n",
        "    train_batches,\n",
        "    epochs=10,\n",
        "    validation_data=test_batches, validation_steps=20)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "LQjpKVYTXU-1"
      },
      "source": [
        "Với cách làm đã nêu, mô hình đạt độ chính xác khoảng 88% trên tập kiểm thử (dễ dàng nhận thấy mô hình trên đang bị overfit vì độ chính xác thu được trên tập huấn luyện cao hơn rất nhiều)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "0D3OTmOT1z1O"
      },
      "outputs": [],
      "source": [
        "import matplotlib.pyplot as plt\n",
        "\n",
        "history_dict = history.history\n",
        "\n",
        "acc = history_dict['accuracy']\n",
        "val_acc = history_dict['val_accuracy']\n",
        "loss=history_dict['loss']\n",
        "val_loss=history_dict['val_loss']\n",
        "\n",
        "epochs = range(1, len(acc) + 1)\n",
        "\n",
        "plt.figure(figsize=(12,9))\n",
        "plt.plot(epochs, loss, 'bo', label='Training loss')\n",
        "plt.plot(epochs, val_loss, 'b', label='Validation loss')\n",
        "plt.title('Training and validation loss')\n",
        "plt.xlabel('Epochs')\n",
        "plt.ylabel('Loss')\n",
        "plt.legend()\n",
        "plt.show()\n",
        "\n",
        "plt.figure(figsize=(12,9))\n",
        "plt.plot(epochs, acc, 'bo', label='Training acc')\n",
        "plt.plot(epochs, val_acc, 'b', label='Validation acc')\n",
        "plt.title('Training and validation accuracy')\n",
        "plt.xlabel('Epochs')\n",
        "plt.ylabel('Accuracy')\n",
        "plt.legend(loc='lower right')\n",
        "plt.ylim((0.5,1))\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "KCoA6qwqP836"
      },
      "source": [
        "## Truy cập các embedding\n",
        "\n",
        "Tiếp theo, ta sẽ lấy ra các word embedding đã được học trong quá trình huấn luyện. Tập hợp các embedding này là một ma trận có kích thước `(số từ trong danh sách từ vựng, số chiều embedding)`."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "t8WwbsXCXtpa"
      },
      "outputs": [],
      "source": [
        "e = model.layers[0]\n",
        "weights = e.get_weights()[0]\n",
        "print(weights.shape) # shape: (vocab_size, embedding_dim)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "J8MiCA77X8B8"
      },
      "source": [
        "Thực hiện lưu lại các trọng số này. Lưu ý rằng để trực quan hóa các embedding thu được với [Embedding Projector](http://projector.tensorflow.org), ta cần hai tệp dữ liệu: một tệp chứa các vector embedding và một tệp chứa meta data (là các từ trong danh sách từ vựng)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "GsjempweP9Lq"
      },
      "outputs": [],
      "source": [
        "import io\n",
        "\n",
        "encoder = info.features['text'].encoder\n",
        "\n",
        "out_v = io.open('vecs.tsv', 'w', encoding='utf-8')\n",
        "out_m = io.open('meta.tsv', 'w', encoding='utf-8')\n",
        "\n",
        "for num, word in enumerate(encoder.subwords):\n",
        "  vec = weights[num+1] # skip 0, it's padding.\n",
        "  out_m.write(word + \"\\n\")\n",
        "  out_v.write('\\t'.join([str(x) for x in vec]) + \"\\n\")\n",
        "out_v.close()\n",
        "out_m.close()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "JQyMZWyxYjMr"
      },
      "source": [
        "Nếu đang chạy hướng dẫn này trên [Colaboratory](https://colab.research.google.com), bạn có thể sử dụng đoạn chương trình sau để tải các tệp này về máy (hoặc sử dụng trình duyệt tệp bằng cách chọn *View -> Table of contents -> File browser*)."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {},
        "colab_type": "code",
        "id": "-gFbbMmvYvhp"
      },
      "outputs": [],
      "source": [
        "try:\n",
        "  from google.colab import files\n",
        "except ImportError:\n",
        "   pass\n",
        "else:\n",
        "  files.download('vecs.tsv')\n",
        "  files.download('meta.tsv')"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "PXLfFA54Yz-o"
      },
      "source": [
        "## Trực quan hóa các embedding\n",
        "\n",
        "Để thực hiện trực quan hóa các embedding thu được, trước tiên ta cần tải chúng lên chương trình embedding projector.\n",
        "\n",
        "Mở [Embedding Projector](http://projector.tensorflow.org/) (hoặc chạy TensorBoard ở local).\n",
        "\n",
        "* Chọn \"Load data\".\n",
        "\n",
        "* Tải lên hai tệp mà ta đã tạo ở trên gồm: `vecs.tsv` và `meta.tsv`.\n",
        "\n",
        "Lúc này các embedding đã huấn luyện sẽ được hiển thị lên màn hình. Bạn có thể sử dụng chức năng tìm kiếm từ để xem các từ khác nằm gần nó nhất. Chẳng hạn, nếu thử với từ \"beautiful\", ta sẽ thấy các từ gần đó chẳng hạn như \"wonderful\".\n",
        "\n",
        "Lưu ý: Kết quả mỗi người thu được có thể khác nhau đôi chút tùy thuộc vào các trọng số được khởi tạo ngẫu nhiên trước khi huấn luyện lớp embedding.\n",
        "\n",
        "Lưu ý: Một số thực nghiệm sử dụng mô hình đơn giản hơn có thể cho kết quả embedding tốt hơn. Hãy thử xóa lớp `Dense(16)`, huấn luyện lại mô hình và trực quan hóa các embedding một lần nữa để kiểm tra.\n",
        "\n",
        "<img src=\"https://github.com/tensorflow/docs/blob/master/site/en/tutorials/text/images/embedding.jpg?raw=1\" alt=\"Screenshot of the embedding projector\" width=\"400\"/>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "iS_uMeMw3Xpj"
      },
      "source": [
        "## Tiếp theo"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "BSgAZpwF5xF_"
      },
      "source": [
        "Trong hướng dẫn này, chúng ta đã đi qua các bước để huấn luyện và trực quan hóa các word embedding từ đầu trên một tập dữ liệu nhỏ.\n",
        "\n",
        "* Để tìm hiểu thêm về mạng hồi quy, xem thêm tại [Keras RNN Guide](../../guide/keras/rnn.ipynb).\n",
        "\n",
        "* Để tìm hiểu thêm về bài toán phân loại văn bản (bao gồm cách thức thực hiện hoặc cho những bạn thắc mắc khi nào nên sử dụng embedding và khi nào nên sử dụng one-hot encoding), hãy xem qua [Hướng dẫn phân loại văn bản](https://developers.google.com/machine-learning/guides/text-classification/step-2-5)."
      ]
    }
  ],
  "metadata": {
    "accelerator": "GPU",
    "colab": {
      "collapsed_sections": [],
      "name": "word_embeddings.ipynb",
      "private_outputs": true,
      "provenance": [],
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
